﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Xml;
using System.IO;

namespace RedmineWorkerDotNET.App.Classes
{
    public class RedmineConfiguration
    {
        private const string CONST_S_ENCRYPTION_KEY = "oared";

        private static XmlConfiguration _objRedmineUserConfiguration = new XmlConfiguration(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "redmine.xml"));
        
        public static bool SaveCredentials
        {
            get { return _objRedmineUserConfiguration.GetBoolSetting("SaveCredentials", Properties.Settings.Default.Redmine_DefaultSaveCredentials); }
            set
            {
                _objRedmineUserConfiguration.SetSetting("SaveCredentials", value); ;
                _objRedmineUserConfiguration.Save();
            }
        }

        public static string Username
        {
            get { return _objRedmineUserConfiguration.GetStringSetting("Username", null); }
            set
            {
                _objRedmineUserConfiguration.SetSetting("Username", value); ;
                _objRedmineUserConfiguration.Save();
            }
        }

        public static string ServerURL
        {
            get 
            { 
                string sServerURL = _objRedmineUserConfiguration.GetStringSetting("ServerURL", null);
                if (sServerURL == null)
                {
                    sServerURL = Properties.Settings.Default.Redmine_DefaultServerUrl;
                    _objRedmineUserConfiguration.SetSetting("ServerURL", sServerURL);
                    _objRedmineUserConfiguration.Save();
                }
                return sServerURL;
            }
            set
            {
                _objRedmineUserConfiguration.SetSetting("ServerURL", value);
                _objRedmineUserConfiguration.Save();
            }
        }

        public static string Password
        {
            get 
            { 
                try
                {
                    string sEncryptedPassword = _objRedmineUserConfiguration.GetStringSetting("Password", null);
                    return Crypto.DecryptStringAES(sEncryptedPassword, CONST_S_ENCRYPTION_KEY);
                }
                catch { }
                return null;
            }
            set
            {
                if (value != null)
                    _objRedmineUserConfiguration.SetSetting("Password", Crypto.EncryptStringAES(value, CONST_S_ENCRYPTION_KEY));
                else
                    _objRedmineUserConfiguration.SetSetting("Password", String.Empty);

                _objRedmineUserConfiguration.Save();
            }
        }

        public static string TimeLogDefaultComment
        {
            get { return _objRedmineUserConfiguration.GetStringSetting("TimeLogDefaultComment", Properties.Settings.Default.TimeLog_DefaultComment); }
            set
            {
                _objRedmineUserConfiguration.SetSetting("TimeLogDefaultComment", value); ;
                _objRedmineUserConfiguration.Save();
            }
        }

        public static string TimeLogDefaultActivity
        {
            get { return _objRedmineUserConfiguration.GetStringSetting("TimeLogDefaultActivity", Properties.Settings.Default.TimeLog_DefaultActivty); }
            set
            {
                _objRedmineUserConfiguration.SetSetting("TimeLogDefaultActivity", value); ;
                _objRedmineUserConfiguration.Save();
            }
        }
    }

    public class XmlConfiguration
    {
        #region Const Values

        private const string CONST_S_DATETIME_FORMAT = "yyyyMMddHHmmss";
        private const string CONST_S_DOCUMENT_TAG_NAME = "configuration";
        private const string CONST_S_PROPERTY_TAG_NAME = "property";
        private const string CONST_S_PROPERTY_ATT_NAME = "name";
        private const string CONST_S_PROPERTY_ATT_VALUE = "value"; 

        #endregion

        #region Attributes

        private string _sFilePath;
        private Dictionary<string, XmlNode> _mapNodesByProperty; 

        #endregion

        #region Initialization

        public XmlConfiguration(string sFilePath)
        {
            _sFilePath = sFilePath;
            _mapNodesByProperty = new Dictionary<string, XmlNode>();

            if (System.IO.File.Exists(_sFilePath))
                Read();
        }

        public void Read()
        {
            _mapNodesByProperty.Clear();

            XmlDocument objReadDocument = new XmlDocument();
            objReadDocument.Load(_sFilePath);

            XmlNodeList lstProperties = objReadDocument.GetElementsByTagName(CONST_S_PROPERTY_TAG_NAME);
            foreach (XmlNode objNode in lstProperties)
            {
                string sPropertyName = GetOptionalStringAttribute(objNode, CONST_S_PROPERTY_ATT_NAME, null);
                if (sPropertyName != null)
                    _mapNodesByProperty[sPropertyName] = objNode;
            }
        } 

        #endregion

        #region Methods

        public void Save()
        {
            string sTempFile = Path.GetTempFileName();
            XmlTextWriter objWriter = new XmlTextWriter(sTempFile, Encoding.Default);
            try
            {
                objWriter.Formatting = Formatting.Indented;
                objWriter.WriteStartDocument();
                objWriter.WriteStartElement(CONST_S_DOCUMENT_TAG_NAME);

                foreach (XmlNode objNode in _mapNodesByProperty.Values)
                {
                    objNode.WriteTo(objWriter);
                }

                objWriter.Flush();

                objWriter.WriteEndElement();
            }
            finally
            {
                objWriter.Close();
            }

            Directory.CreateDirectory(Path.GetDirectoryName(_sFilePath));
            File.Copy(sTempFile, _sFilePath, true);
            File.Delete(sTempFile);
        }

        public void SetSetting(string sPropertyName, string sValue)
        {
            XmlNode objNode = CreateEmptyPropertyNode();
            SetStringAttribute(objNode, CONST_S_PROPERTY_ATT_NAME, sPropertyName);
            SetStringAttribute(objNode, CONST_S_PROPERTY_ATT_VALUE, sValue);
            _mapNodesByProperty[sPropertyName] = objNode;
        }

        public void SetSetting(string sPropertyName, bool bValue)
        {
            XmlNode objNode = CreateEmptyPropertyNode();
            SetStringAttribute(objNode, CONST_S_PROPERTY_ATT_NAME, sPropertyName);
            SetBooleanAttribute(objNode, CONST_S_PROPERTY_ATT_VALUE, bValue);
            _mapNodesByProperty[sPropertyName] = objNode;
        }

        public void SetSetting(string sPropertyName, int iValue)
        {
            XmlNode objNode = CreateEmptyPropertyNode();
            SetStringAttribute(objNode, CONST_S_PROPERTY_ATT_NAME, sPropertyName);
            SetIntegerAttribute(objNode, CONST_S_PROPERTY_ATT_VALUE, iValue);
            _mapNodesByProperty[sPropertyName] = objNode;
        }

        public void SetSetting(string sPropertyName, double dValue)
        {
            XmlNode objNode = CreateEmptyPropertyNode();
            SetStringAttribute(objNode, CONST_S_PROPERTY_ATT_NAME, sPropertyName);
            SetDoubleAttribute(objNode, CONST_S_PROPERTY_ATT_VALUE, dValue);
            _mapNodesByProperty[sPropertyName] = objNode;
        }

        public void SetSetting(string sPropertyName, DateTime dtValue)
        {
            XmlNode objNode = CreateEmptyPropertyNode();
            SetStringAttribute(objNode, CONST_S_PROPERTY_ATT_NAME, sPropertyName);
            SetDateTimeAttribute(objNode, CONST_S_PROPERTY_ATT_VALUE, dtValue);
            _mapNodesByProperty[sPropertyName] = objNode;
        }

        public string GetStringSetting(string sProperty, string sDefaultValue)
        {
            if (_mapNodesByProperty.ContainsKey(sProperty))
                return GetOptionalStringAttribute(_mapNodesByProperty[sProperty], CONST_S_PROPERTY_ATT_VALUE, sDefaultValue);
            return sDefaultValue;
        }

        public bool GetBoolSetting(string sProperty, bool bDefaultValue)
        {
            if (_mapNodesByProperty.ContainsKey(sProperty))
                return GetOptionalBooleanAttribute(_mapNodesByProperty[sProperty], CONST_S_PROPERTY_ATT_VALUE, bDefaultValue);
            return bDefaultValue;
        }

        public int GetIntSetting(string sProperty, int iDefaultValue)
        {
            if (_mapNodesByProperty.ContainsKey(sProperty))
                return GetOptionalIntegerAttribute(_mapNodesByProperty[sProperty], CONST_S_PROPERTY_ATT_VALUE, iDefaultValue);
            return iDefaultValue;
        }

        public double GetDoubleSetting(string sProperty, double dDefaultValue)
        {
            if (_mapNodesByProperty.ContainsKey(sProperty))
                return GetOptionalDoubleAttribute(_mapNodesByProperty[sProperty], CONST_S_PROPERTY_ATT_VALUE, dDefaultValue);
            return dDefaultValue;
        }

        public DateTime GetDateSetting(string sProperty, DateTime dtDefaultValue)
        {
            if (_mapNodesByProperty.ContainsKey(sProperty))
                return GetOptionalDateTimeAttribute(_mapNodesByProperty[sProperty], CONST_S_PROPERTY_ATT_VALUE, dtDefaultValue);
            return dtDefaultValue;
        } 

        #endregion

        #region Auxiliar Methods

        private static XmlDocument _objDocument = new XmlDocument();

        private static XmlNode CreateEmptyPropertyNode()
        {
            return _objDocument.CreateElement(CONST_S_PROPERTY_TAG_NAME);
        }

        private static XmlAttribute CreateAttribute(string sName, string sValue)
        {
            XmlAttribute objAtt = _objDocument.CreateAttribute(sName);
            objAtt.Value = sValue;
            return objAtt;
        }

        private static void AppendStringAttribute(XmlNode objNode, string sAttName, string sValue)
        {
            XmlAttribute objAtt = CreateAttribute(sAttName, sValue.Replace("\r", String.Empty).Replace("\n", String.Empty));
            objNode.Attributes.Append(objAtt);
        }

        private static void AppendBooleanAttribute(XmlNode objNode, string sAttName, Boolean bValue)
        {
            XmlAttribute objAtt = CreateAttribute(sAttName, bValue.ToString(System.Globalization.CultureInfo.InvariantCulture));
            objNode.Attributes.Append(objAtt);
        }

        private static void AppendIntegerAttribute(XmlNode objNode, string sAttName, int iValue)
        {
            XmlAttribute objAtt = CreateAttribute(sAttName, iValue.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat));
            objNode.Attributes.Append(objAtt);
        }

        private static void AppendDoubleAttribute(XmlNode objNode, string sAttName, double dValue)
        {
            XmlAttribute objAtt = CreateAttribute(sAttName, dValue.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat));
            objNode.Attributes.Append(objAtt);
        }

        private static void AppendLongAttribute(XmlNode objNode, string sAttName, long lValue)
        {
            XmlAttribute objAtt = CreateAttribute(sAttName, lValue.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat));
            objNode.Attributes.Append(objAtt);
        }

        private static void AppendDateTimeAttribute(XmlNode objNode, string sAttName, DateTime dtValue)
        {
            XmlAttribute objAtt = CreateAttribute(sAttName, ConvertDateToString(dtValue));
            objNode.Attributes.Append(objAtt);
        }

        private static void SetStringAttribute(XmlNode objNode, string sAttName, string sValue)
        {
            if (objNode.Attributes[sAttName] != null)
                objNode.Attributes[sAttName].Value = sValue;
            else
                AppendStringAttribute(objNode, sAttName, sValue);
        }

        private static void SetBooleanAttribute(XmlNode objNode, string sAttName, Boolean bValue)
        {
            if (objNode.Attributes[sAttName] != null)
                objNode.Attributes[sAttName].Value = bValue.ToString(System.Globalization.CultureInfo.InvariantCulture);
            else
                AppendBooleanAttribute(objNode, sAttName, bValue);
        }

        private static void SetIntegerAttribute(XmlNode objNode, string sAttName, int iValue)
        {
            if (objNode.Attributes[sAttName] != null)
                objNode.Attributes[sAttName].Value = iValue.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
            else
                AppendIntegerAttribute(objNode, sAttName, iValue);
        }

        private static void SetDoubleAttribute(XmlNode objNode, string sAttName, double dValue)
        {
            if (objNode.Attributes[sAttName] != null)
                objNode.Attributes[sAttName].Value = dValue.ToString(System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
            else
                AppendDoubleAttribute(objNode, sAttName, dValue);
        }

        private static void SetDateTimeAttribute(XmlNode objNode, string sAttName, DateTime dtValue)
        {
            if (objNode.Attributes[sAttName] != null)
                objNode.Attributes[sAttName].Value = dtValue.ToUniversalTime().ToString(CONST_S_DATETIME_FORMAT);
            else
                AppendDateTimeAttribute(objNode, sAttName, dtValue);
        }

        private static string GetStringAttribute(XmlNode objNode, string sAttName)
        {
            return objNode.Attributes[sAttName].Value;
        }

        private static long GetLongAttribute(XmlNode objNode, string sAttName)
        {
            return long.Parse(objNode.Attributes[sAttName].Value, System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
        }

        private static Boolean GetBooleanAttribute(XmlNode objNode, string sAttName)
        {
            return Boolean.Parse(objNode.Attributes[sAttName].Value);
        }

        private static int GetIntegerAttribute(XmlNode objNode, string sAttName)
        {
            return Int32.Parse(objNode.Attributes[sAttName].Value, System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
        }

        private static double GetDoubleAttribute(XmlNode objNode, string sAttName)
        {
            return Double.Parse(objNode.Attributes[sAttName].Value, System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
        }

        private static DateTime GetDatetimeAttribute(XmlNode objNode, string sAttName)
        {
            return DateTime.ParseExact(objNode.Attributes[sAttName].Value, CONST_S_DATETIME_FORMAT, System.Globalization.CultureInfo.InvariantCulture);
        }

        private static string GetOptionalStringAttribute(XmlNode objNode, string sAttName, string sDefault)
        {
            if (objNode.Attributes[sAttName] != null)
                return GetStringAttribute(objNode, sAttName);
            return sDefault;
        }

        private static Boolean GetOptionalBooleanAttribute(XmlNode objNode, string sAttName, Boolean bDefault)
        {
            if (objNode.Attributes[sAttName] != null)
            {
                Boolean bValue = bDefault;
                if (Boolean.TryParse(objNode.Attributes[sAttName].Value, out bValue))
                    return bValue;
            }
            return bDefault;
        }

        private static int GetOptionalIntegerAttribute(XmlNode objNode, string sAttName, int iDefault)
        {
            if (objNode.Attributes[sAttName] != null)
            {
                int iValue = iDefault;
                if (Int32.TryParse(objNode.Attributes[sAttName].Value, System.Globalization.NumberStyles.Float | System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture.NumberFormat, out iValue))
                    return iValue;
            }
            return iDefault;
        }

        private static long GetOptionalLongAttribute(XmlNode objNode, string sAttName, long lDefault)
        {
            if (objNode.Attributes[sAttName] != null)
            {
                long lValue = lDefault;
                if (long.TryParse(objNode.Attributes[sAttName].Value, System.Globalization.NumberStyles.Float | System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture.NumberFormat, out lValue))
                    return lValue;
            }
            return lDefault;
        }

        private static double GetOptionalDoubleAttribute(XmlNode objNode, string sAttName, double dDefault)
        {
            if (objNode.Attributes[sAttName] != null)
            {
                double dValue = dDefault;
                if (Double.TryParse(objNode.Attributes[sAttName].Value, System.Globalization.NumberStyles.Float | System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture.NumberFormat, out dValue))
                    return dValue;
            }
            return dDefault;
        }

        private static DateTime GetOptionalDateTimeAttribute(XmlNode objNode, string sAttName, DateTime dtDefault)
        {
            if (objNode.Attributes[sAttName] != null)
            {
                DateTime dtValue = dtDefault.ToUniversalTime();
                if (DateTime.TryParseExact(objNode.Attributes[sAttName].Value, CONST_S_DATETIME_FORMAT, System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat, System.Globalization.DateTimeStyles.AssumeUniversal, out dtValue))
                    return dtValue.ToUniversalTime();
            }
            return dtDefault;
        }

        public static string ConvertDateToString(DateTime dtValue)
        {
            return dtValue.ToUniversalTime().ToString(CONST_S_DATETIME_FORMAT);
        }

        #endregion
    }
}
